/*
  AdViEmulator - AdventureVision emulator
  Copyright (C) 2012-2013  JustBurn

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <string.h>
#include "hardwarewindow.h"
#include "ui_hardwarewindow.h"

enum {
	HEXIO_P1, HEXIO_P2, HEXIO_TCNT,
	HEXIO_DISP0, HEXIO_DISP1, HEXIO_DISP2, HEXIO_DISP3, HEXIO_DISP4,
	COMBO_TCNT, COMBO_XRAMBANK, COMBO_P2MODE,
	CHECK_BIOS, CHECK_TCNTE, CHECK_TOF
};

#define CONN_HEXIO(c, e) { connect(c, SIGNAL(valueChanged(int)), this, SLOT(hexioChanged(int))); c->setProperty("type", QVariant(e)); }
#define CONN_COMBO(c, e) { connect(c, SIGNAL(currentIndexChanged(int)), this, SLOT(comboChanged(int))); c->setProperty("type", QVariant(e)); }
#define CONN_ABUTTON(c, e) { connect(c, SIGNAL(toggled(bool)), this, SLOT(abuttonChanged(bool))); c->setProperty("type", QVariant(e)); }

HardwareWindow::HardwareWindow(QWidget *parent, AdViEmulator *emu)
	: BaseViewWindow(parent)
	, refreshTmr(new QTimer(this))
	, ui(new Ui::HardwareWindow)
{
	ui->setupUi(this);
	avemu = emu;
	connect(refreshTmr, SIGNAL(timeout()), this, SLOT(refreshWindow()));
	connect(parent, SIGNAL(emuStateChanged(int)), this, SLOT(emuStateChanged(int)));
	connect(ui->pushStart, SIGNAL(clicked()), parent, SLOT(OnMenuStartEmu()));
	connect(ui->pushStop, SIGNAL(clicked()), parent, SLOT(OnMenuStopEmu()));
	connect(ui->pushStep, SIGNAL(clicked()), parent, SLOT(OnMenuCPUStepEmu()));
	connect(ui->pushReset, SIGNAL(clicked()), parent, SLOT(OnMenuResetEmu()));
	refreshTmr->start(500);
	ui->displayLEDs->attachEmu(emu);
	refreshRegisters();
	adjustSize();
	setFixedSize(size());
	// Connect signals
	CONN_HEXIO(ui->lineP1, HEXIO_P1);
	CONN_HEXIO(ui->lineP2, HEXIO_P2);
	CONN_HEXIO(ui->lineTCNT, HEXIO_TCNT);
	CONN_HEXIO(ui->lineDisplay0, HEXIO_DISP0);
	CONN_HEXIO(ui->lineDisplay1, HEXIO_DISP1);
	CONN_HEXIO(ui->lineDisplay2, HEXIO_DISP2);
	CONN_HEXIO(ui->lineDisplay3, HEXIO_DISP3);
	CONN_HEXIO(ui->lineDisplay4, HEXIO_DISP4);
	CONN_COMBO(ui->comboTCNT, COMBO_TCNT);
	CONN_COMBO(ui->comboXRAMBank, COMBO_XRAMBANK);
	CONN_COMBO(ui->comboP2Mode, COMBO_P2MODE);
	CONN_ABUTTON(ui->checkBIOS, CHECK_BIOS);
	CONN_ABUTTON(ui->checkTCNTI, CHECK_TCNTE);
	CONN_ABUTTON(ui->checkTOF, CHECK_TOF);
}

HardwareWindow::~HardwareWindow()
{
	refreshTmr->stop();
	delete refreshTmr;
	delete ui;
}

void HardwareWindow::refreshWindow(void)
{
	if (avemu->IsRunning()) {
		refreshRegisters();
	}
	ui->displayLEDs->update();
}

void HardwareWindow::refreshRegisters(void)
{
	ui->labelCountCyc->setText(QString::number(avemu->cycCnt0));
	ui->lineP1->setValue(avemu->GetP1());
	ui->lineP2->setValue(avemu->GetP2());
	ui->lineTCNT->setValue(avemu->cpu.tcnt);
	ui->lineDisplay0->setValue(avemu->displayPre[0]);
	ui->lineDisplay1->setValue(avemu->displayPre[1]);
	ui->lineDisplay2->setValue(avemu->displayPre[2]);
	ui->lineDisplay3->setValue(avemu->displayPre[3]);
	ui->lineDisplay4->setValue(avemu->displayPre[4]);
	ui->labelPinT1->setText("T1 = " + QString::number(avemu->cpu.gio_t1));
	ui->comboTCNT->setCurrentIndex(avemu->cpu.tmrsel);
	ui->comboXRAMBank->setCurrentIndex(avemu->GetP1() & 3);
	ui->comboP2Mode->setCurrentIndex(avemu->GetP2() >> 5);
	ui->checkBIOS->setChecked(avemu->GetP1() & 4);
	ui->checkTCNTI->setChecked(avemu->cpu.ff.tcnti);
	ui->checkTOF->setChecked(avemu->cpu.ff.tf);
	ui->labelRefreshS->setText("Refresh/s: " + QString::number(avemu->frameCnt));
}

void HardwareWindow::emuStateChanged(int)
{
	refreshRegisters();
	refreshWindow();
}

void HardwareWindow::hexioChanged(int value)
{
	switch(sender()->property("type").toInt()) {
	case HEXIO_P1: avemu->cpu.OnPortWr(1, value); break;
	case HEXIO_P2: avemu->cpu.OnPortWr(2, value); break;
	case HEXIO_TCNT: avemu->cpu.tcnt = value; break;
	case HEXIO_DISP0: avemu->displayPre[0] = value; break;
	case HEXIO_DISP1: avemu->displayPre[1] = value; break;
	case HEXIO_DISP2: avemu->displayPre[2] = value; break;
	case HEXIO_DISP3: avemu->displayPre[3] = value; break;
	case HEXIO_DISP4: avemu->displayPre[4] = value; break;
	}
	refreshRegisters();
	refreshWindow();
}

void HardwareWindow::comboChanged(int index)
{
	switch(sender()->property("type").toInt()) {
	case COMBO_TCNT: avemu->cpu.tmrsel = index; break;
	case COMBO_XRAMBANK: avemu->cpu.OnPortWr(1, (avemu->GetP1() & 0xFC) | index); break;
	case COMBO_P2MODE: avemu->cpu.OnPortWr(2, (avemu->GetP2() & 0x1F) | (index << 5)); break;
	}
	refreshRegisters();
	refreshWindow();
}

void HardwareWindow::abuttonChanged(bool checked)
{
	switch(sender()->property("type").toInt()) {
	case CHECK_BIOS: avemu->cpu.OnPortWr(1, (avemu->GetP1() & 0xFB) | (checked ? 4 : 0)); break;
	case CHECK_TCNTE: avemu->cpu.ff.tcnti = checked ? 1 : 0; break;
	case CHECK_TOF: avemu->cpu.ff.tf = checked ? 1 : 0; break;
	}
	refreshRegisters();
	refreshWindow();
}

void HardwareWindow::cycClear()
{
	avemu->cycCnt0 = 0;
	refreshRegisters();
}

void HardwareWindow::latchDisplay()
{
	uint8_t p2stat = avemu->GetP2();
	avemu->cpu.OnPortWr(2, 0x10);
	avemu->cpu.OnPortWr(2, p2stat);
	refreshWindow();
}

void HardwareWindow::clearDisplay()
{
	uint8_t p2stat = avemu->GetP2();
	avemu->cpu.OnPortWr(2, 0x00);
	avemu->cpu.OnPortWr(2, p2stat);
	refreshWindow();
}
